package cs235.xml;

public abstract class XMLNode implements Node, Cloneable {

    private Node parent;
    private final int type;
    private Node firstChild;
    private Node lastChild;
    private Node previousSibling;
    private Node nextSibling;

    public XMLNode(int type, Node parent) {
        this.type = type;
        this.parent = parent;
    }

    @Override
    public int getType() {
        return type;
    }

    @Override
    public Node getParent() {
        return parent;
    }

    public final void setParent(Node parent) {
        this.parent = parent;
    }

    @Override
    public Node getFirstChild() {
        return firstChild;
    }

    public void setFirstChild(Node firstChild) {
        this.firstChild = firstChild;
    }

    @Override
    public Node getLastChild() {
        return lastChild;
    }

    public void setLastChild(Node lastChild) {
        this.lastChild = lastChild;
    }

    @Override
    public Node getNextSibling() {
        return nextSibling;
    }

    public void setPreviousSibling(Node previousSibling) {
        if (previousSibling != null && previousSibling.getParent() != parent) {
            ((XMLNode) previousSibling).setParent(parent);
        }
        this.previousSibling = previousSibling;
    }

    @Override
    public Node getPreviousSibling() {
        return previousSibling;
    }

    @Override
    public final void insertChildBefore(Node newChild, Node child) {
        Node before = child.getPreviousSibling();
        if (firstChild == child){
            firstChild = newChild;
        }
        if (before != null){
            ((XMLNode) before).setNextSibling(newChild);
            ((XMLNode) newChild).setPreviousSibling(before);
        }
        ((XMLNode) child).setPreviousSibling(newChild);
        ((XMLNode) newChild).setNextSibling(child);
    }

    @Override
    public void appendChild(Node newChild) {
        if (newChild.getParent()!=this){
            ((XMLNode) newChild).setParent(this);
        }
        if (firstChild == null){
            setFirstChild(newChild);
            setLastChild(newChild);
        } else {
            ((XMLNode) lastChild).setNextSibling(newChild);
            ((XMLNode) newChild).setPreviousSibling(lastChild);
            setLastChild(newChild);
        }
    }

    public void setNextSibling(Node nextSibling) {
        if (nextSibling != null && nextSibling.getParent() != parent) {
            ((XMLNode) nextSibling).setParent(parent);
        }
        this.nextSibling = nextSibling;
    }

    @Override
    public void removeChild(Node child) {
        Node previous = child.getPreviousSibling();
        Node next = child.getNextSibling();
        
        if  (previous != null){
            ((XMLNode) previous).setNextSibling(next);
        }
        
        if (next != null){
            ((XMLNode) next).setPreviousSibling(previous);
        }
        
        if (child == firstChild && child == lastChild ){
            firstChild = null;
            lastChild = null;
        }
        
        if (child == firstChild){
            firstChild = previous;
        }
        if (child == lastChild){
            lastChild = next;
        }
    }

    @Override
    public void replaceChild(Node newChild, Node oldChild) {
        insertChildBefore(newChild, oldChild);
        removeChild(oldChild);
    }
}
